package com.systop.asset.service.impl;

import com.systop.asset.entity.*;
import com.systop.asset.entity.Process;
import com.systop.asset.entity.vo.ProcessVo;
import com.systop.asset.enums.HttpCodeEnum;
import com.systop.asset.exception.SystemException;
import com.systop.asset.mapper.DepartmentMapper;
import com.systop.asset.mapper.UserMapper;
import com.systop.asset.service.IWorkFlowService;
import jakarta.annotation.Resource;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * 工作流操作业务层
 */
@Service
public class WorkFlowServiceImpl implements IWorkFlowService {
    /**
     * Activiti 的资源管理类，该服务负责部署流程定义，管理流程资源。
     * 在使用 Activiti 时，一开始需要先完成流程部署，即将使用建模工具设计的业务流程图通过 RepositoryService 进行部署
     */
    @Resource
    private RepositoryService repositoryService;
    /**
     * Activiti 的任务管理类，用于处理业务运行中的各种任务，例如查询分给用户或组的任务、创建新的任务、分配任务、确定和完成一个任务
     */
    @Resource
    private TaskService taskService;
    /**
     * Activiti 的流程运行管理类，用于开始一个新的流程实例，获取关于流程执行的相关信息。
     * 流程定义用于确定一个流程中的结构和各个节点间行为，而流程实例则是对应的流程定义的一个执行，可以理解为 Java 中类和对象的关系
     */
    @Resource
    private RuntimeService runtimeService;
    /**
     * Activiti 的历史管理类，可以查询历史信息。
     */
    @Resource
    private HistoryService historyService;
    @Resource
    private UserMapper userMapper;
    @Resource
    private DepartmentMapper departmentMapper;
    //文件上传的根路径
    @Value("${upload.path}")
    private String rootPath;

    /**
     * 部署流程
     * @return
     */
    public RestResponse deployProcess(ProcessTemplate template){
        // 定义zip输入流
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(rootPath+template.getProcessDefinitionPath());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return RestResponse.failure(600,"流程文件加载失败");
        }
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);

        // 流程部署
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .name(template.getTemplateName())
                .deploy();
        System.out.println("流程部署id：" + deployment.getId());
        System.out.println("流程部署名称：" + deployment.getName());
        //设置流程id
        template.setDeploymentId(deployment.getId());
        // 获取部署的流程定义
        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .list();

        // 获取流程定义Key
        if (!processDefinitions.isEmpty()) {
            String processDefinitionKey = processDefinitions.get(0).getKey();
            String processDefinitionVersion = processDefinitions.get(0).getVersion()+"";
            System.out.println("流程定义Key: " + processDefinitionKey);
            System.out.println("流程定义version: " +  processDefinitions.get(0).getVersion());
            //设置流程部署的版本号
            template.setProcessVersion(processDefinitionVersion);
            template.setProcessDefinitionKey(processDefinitionKey); //流程部署key
        }
        RestResponse<Object> response = RestResponse.success();
        response.setMessage("流程部署成功");
        return response;

    }

    /**
     * 删除流程
     * @param template
     */
    @Override
    public void deleteProcess(ProcessTemplate template) {
        //删除文件
        File file = new File(rootPath,template.getProcessDefinitionPath());
        if (file.exists()){
            file.delete();
        }
        //流程部署id
        String deploymentId = template.getDeploymentId();
        //根据流程部署id删除流程： 如果该流程定义已有流程实例启动则删除时会抛出异常
        repositoryService.deleteDeployment(deploymentId);
        //设置true 级联删除流程定义，即使该流程有流程实例启动也可以删除，设置为false非级别删除方式
        //repositoryService.deleteDeployment(deploymentId, true);
    }

    /**
     * 查询流程文件
     * @param template 模板对象
     * @param type 文件类型
     * @return
     */
    @Override
    public InputStream selectBpmnFile(ProcessTemplate template, String type) {
        //根据流程id查询流程定义对象
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(template.getDeploymentId()).singleResult();
        //获取资源名称
        String resourceName = definition.getResourceName();
        if ("xml".equals(type)){
            return repositoryService.getResourceAsStream(definition.getDeploymentId(),resourceName);
        }
        System.out.println(definition.getDiagramResourceName());//图片资源名称
        resourceName = resourceName.replace(".xml",".png");
        return repositoryService.getResourceAsStream(definition.getDeploymentId(),resourceName);
    }

    /**
     * 开启审批流程
     * @param process
     * @param template
     * @return
     */
    @Override
    public ProcessRecord publishProcess(Process process,ProcessTemplate template) {
        //绑定业务id：绑定审核表的id
        String businessKey = String.valueOf(process.getId());
        //查询员工部门
        Department department =  departmentMapper.selectDepartmentById(process.getDeptId());
        //查询员工领导
        User leader = userMapper.selectUserById(department.getManagerId());
        //流程参数
        Map<String, Object> variables = new HashMap<>();
        variables.put("leader",leader.getId());//设置主管为4
        variables.put("manager","管理员");//设置管理员

        //启动流程：根据流程定义key、业务id、流程参数
        ProcessInstance processInstance =
                runtimeService.startProcessInstanceByKey(template.getProcessDefinitionKey(), businessKey, variables);
        //业务表关联当前流程实例id
        String processInstanceId = processInstance.getId();
        process.setProcessInstanceId(processInstanceId);
        //设置审核数据
        process.setDescription("等待" + leader.getUsername() + "审批");
        process.setCurrentAuditor(leader.getUsername()); //设置当前的审核人
        //process.setTitle(leader.getUsername()+"发起" + template.getTemplateName());
        //不需要新增审核记录
        return null;
    }

    /**
     * 流程审核
     * @param process 审批流程对象
     * @param vo 审核数据
     * @return
     */
    @Override
    public ProcessRecord approveProcess(Process process, ProcessVo vo) {
        //任务负责人
        String assignee = vo.getUserId()+"";
        User user = userMapper.selectUserById(vo.getUserId());
        //获取审批任务
        Task task = taskService.createTaskQuery()
                .processInstanceBusinessKey(process.getId()+"") //通过业务id查询任务
                //.taskAssignee(assignee)  //要查询的负责人
                .singleResult();//返回一条
        System.out.println("任务id："+task.getId());

        //设置局部变量，因变量是局部的，所以每个连线对应的变量可以重名（通过排他网关控制审核是否通过）
        taskService.setVariable(task.getId(),"result",vo.getResult());
        //完成任务,参数：任务id，
        taskService.complete(task.getId());
        //创建审核记录对象
        ProcessRecord record = new ProcessRecord();
        record.setProcessId(process.getId());
        //用户名称
        record.setOperateUser(user.getUsername());
        record.setOperateUserId(vo.getUserId());

        //获取任务的下一个负责人：
        Task task2 = taskService.createTaskQuery()
                .processInstanceBusinessKey(process.getId()+"") //通过业务id查询任务
                //.taskAssignee(assignee)  //要查询的负责人
                .singleResult();//返回一条

        //判断任务是否通过
        if (task2 == null){ //为空任务结束
            if (vo.getResult()==1){ //审核通过
                process.setStatus(2); //审核通过：任务结束
                record.setStatus(2L);
                process.setDescription(user.getUsername()+ "审批通过");
                record.setDescription(user.getUsername()+ "审批通过："+vo.getDescription());
            }else{
                process.setStatus(-1); //审核未通过：任务结束
                record.setStatus(-1L);
                process.setDescription(user.getUsername()+ "驳回审批");
                record.setDescription(user.getUsername()+ "驳回审批："+vo.getDescription());
            }
        }else{//不为空，继续后续审核
            //继续审核
            process.setDescription("等待" + task2.getAssignee()+ "审批");
            process.setCurrentAuditor(task2.getAssignee()); //设置当前的审核人
            /*if (vo.getResult()==1){ //审核通过
                //继续审核
                process.setDescription("等待" + task2.getAssignee()+ "审批");
                process.setCurrentAuditor(task2.getAssignee()); //设置当前的审核人
                record.setStatus(1L);
            }else{
                process.setStatus(-1); //审核通过：任务结束
                record.setStatus(-1L);
                process.setDescription(task2.getAssignee()+ "驳回审批");
            }*/
        }

        return record;
    }

    /**
     * 查询待审批列表
     * @param userId 用户id
     * @return 审批id列表
     */
    public List<Long> findPendingTaskList(Long userId){
        //任务负责人:
        String assignee = userId+"";
        //查询用户
        User user = userMapper.selectUserById(userId);
        //当前用户待处理的任务
        List<Long> businessKeys = new ArrayList<>();

        //根据用户id查询部门
        Department department = new Department();
        department.setManagerId(userId);
        List<Department> departments = departmentMapper.selectDepartmentList(department);
        //判断是否是部门负责人：如果是则需要根据id查询
        if (departments!=null&&departments.size()>0){
            //查询任务
            List<Task> list = taskService.createTaskQuery()
                    .taskAssignee(assignee)//只查询该任务负责人的任务
                    .list();
            //获取业务key
            for (Task task : list) {
                /*System.out.println("流程实例id：" + task.getProcessInstanceId());
                System.out.println("任务id：" + task.getId());
                System.out.println("任务负责人：" + task.getAssignee());
                System.out.println("任务名称：" + task.getName());
                System.out.println("业务key：" + task.getBusinessKey());*/
                //查询数据进行封装
                businessKeys.add(Long.valueOf( task.getBusinessKey()));
            }
        }

       /* if (department.getManagerId() == userId){

        }*/

        //判断是否是管理员
        if (user.getUserType()!=null && "0".equals(user.getUserType())){
            assignee = "管理员";
            //查询任务
            List<Task> list = taskService.createTaskQuery()
                    .taskAssignee(assignee)//只查询该任务负责人的任务
                    .list();
            //获取业务key
            for (Task task : list) {
            /*System.out.println("流程实例id：" + task.getProcessInstanceId());
            System.out.println("任务id：" + task.getId());
            System.out.println("任务负责人：" + task.getAssignee());
            System.out.println("任务名称：" + task.getName());
            System.out.println("业务key：" + task.getBusinessKey());*/
                //查询数据进行封装
                businessKeys.add(Long.valueOf( task.getBusinessKey()));
            }
        }

        return businessKeys;
    }

    /**
     * 撤回
     * @param process
     * @return
     */
    @Override
    public ProcessRecord revokeProcess(Process process) {
        //查询用户信息
        User user = userMapper.selectUserById(process.getUserId());
        // 2. 检查流程状态是否可撤回（进行中且未完成）
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(process.getProcessInstanceId())
                .singleResult();
        //判断流程是否已经完成
        if (processInstance == null) {
            HistoricProcessInstance historicProcess = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(process.getProcessInstanceId())
                    .singleResult();
            if (historicProcess != null && historicProcess.getEndTime() != null) {
                throw new SystemException(HttpCodeEnum.REVOKE_PROCESS_ERR);//撤销失败，申请已完成
            }
        }
        // 3. 终止当前流程实例
        runtimeService.deleteProcessInstance(process.getProcessInstanceId(),"申请人撤回");
        process.setStatus(3);//撤回申请
        process.setDescription("申请人撤回申请");

        ProcessRecord record = new ProcessRecord();
        record.setProcessId(process.getId());
        //用户名称
        record.setOperateUser(user.getUsername());
        record.setOperateUserId(process.getUserId());
        record.setDescription(user.getUsername()+ "撤回申请");
        record.setStatus(3L);
        return record;
    }

}
